package com.crm.service.impl;

import com.auth0.jwt.JWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.crm.constant.BusinessException;
import com.crm.constant.dto.BaseFxResponse;
import com.crm.constant.dto.ResultCode;
import com.crm.constant.enums.EmployeeProjectStatusEnum;
import com.crm.constant.enums.ProjectStatusChangeEnum;
import com.crm.constant.enums.ProjectStatusEnum;
import com.crm.entity.*;
import com.crm.mapper.CrmProjectMapper;
import com.crm.model.bo.ProjectBo;
import com.crm.model.bo.ProjectItemBo;
import com.crm.model.bo.ProjectPageBo;
import com.crm.model.dto.BasePageResult;
import com.crm.model.vo.*;
import com.crm.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.crm.constant.dto.ResultCode.EXCEPTION_STATUS_FAIL;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author CRM_SYSTEM
 * @since 2022-03-30
 */
@Service
@Slf4j
public class CrmProjectServiceImpl extends ServiceImpl<CrmProjectMapper, CrmProject> implements ICrmProjectService {
    @Autowired
    private ICrmProjectItemService projectItemService;
    @Autowired
    private ICrmSiteService siteService;
    @Autowired
    private ICrmEmployeeProjectService employeeProjectService;
    @Autowired
    private ICrmEmployeeService employeeService;

    /**
     * 判断所选场地在该时间段是否被使用
     *
     * @param projectBo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public BaseFxResponse<Long> saveProject(ProjectBo projectBo) {

        if (projectBo.getOrderStarterTime().isAfter(projectBo.getProjectStarterTime()) ||
                projectBo.getOrderEndTime().isAfter(projectBo.getProjectEndTime())) {
            return BaseFxResponse.fail(ResultCode.EXCEPTION_PROJECT_DATE);
        }
        CrmProject project = new CrmProject();
        BeanUtils.copyProperties(projectBo, project);
        project.setProjectCode(createProjectNo());
        project.setIsFullNumber(0);
        project.setCurrentNumber(0);
        project.setProjectStatusName(ProjectStatusEnum.PROJECT_NOT_START.getValue());
        project.setProjectStatusCode(ProjectStatusEnum.PROJECT_NOT_START.getCode());

        if (!checkSiteDate(project)) {
            throw new BusinessException(ResultCode.EXCEPTION_PROJECT_SITE_DATE.getCode(), ResultCode.EXCEPTION_PROJECT_SITE_DATE.getMessage());
        }
        Long employeeId = queryEmployeeId();
        project.setCreateEmployeeId(employeeId);
        this.save(project);
        if (CollectionUtils.isNotEmpty(projectBo.getProjectItemBoList())) {
            List<CrmProjectItem> crmProjectItems = new ArrayList<>(32);
            List<ProjectItemBo> itemBoList = projectBo.getProjectItemBoList();
            for (int i = 0; i < itemBoList.size(); i++) {
                CrmProjectItem crmProjectItem = new CrmProjectItem();
                ProjectItemBo projectItemBo = itemBoList.get(i);
                BeanUtils.copyProperties(projectItemBo, crmProjectItem);
                crmProjectItem.setSort(i);
                crmProjectItem.setProjectId(project.getId());
                crmProjectItems.add(crmProjectItem);
            }
            projectItemService.saveBatch(crmProjectItems);
        }
        return BaseFxResponse.success(project.getId());
    }

    public Long queryEmployeeId(){
        if (RequestContextHolder.getRequestAttributes()==null){
            return null;
        }

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        String token = request.getHeader("token");
        String phone = null;
        if (token != null) {
            phone = JWT.decode(token).getAudience().get(1);
        }
        EmployeeVo employeeVo = employeeService.queryByPhone(phone);
        if (Objects.nonNull(employeeVo)){
            return employeeVo.getId();
        }
        return null;
    }

    @Override
    public BaseFxResponse<ProjectVo> queryPorById(Long id) {
        ProjectVo project = null;
        CrmProject crmProject = this.getById(id);
        if (Objects.nonNull(crmProject)) {
            project = new ProjectVo();
            BeanUtils.copyProperties(crmProject, project);
            List<CrmProjectItem> crmProjectItems = projectItemService.getByProjectCode(crmProject.getId());
            List<ProjectItemVo> itemBoList = crmProjectItems.stream().map(r -> {
                ProjectItemVo projectItemBo = new ProjectItemVo();
                BeanUtils.copyProperties(r, projectItemBo);
                return projectItemBo;
            }).collect(Collectors.toList());
            project.setProjectItemBoList(itemBoList);
            if (crmProject.getSiteId() != null) {
                CrmSite crmSite = siteService.getById(crmProject.getSiteId());
                project.setSiteName(crmSite.getSiteName());
            }
        }
        return BaseFxResponse.success(project);
    }

    @Override
    public BaseFxResponse<BasePageResult<ProjectVo>> queryPage(ProjectPageBo pageBo) {
        IPage<ProjectVo> pageParam = new Page<>(pageBo.getPageNumber(), pageBo.getPageSize());
        IPage<ProjectVo> projectVoIPage = baseMapper.queryPage(pageParam, pageBo);
        if (CollectionUtils.isNotEmpty(projectVoIPage.getRecords())) {
            List<ProjectVo> records = projectVoIPage.getRecords();
            List<Long> idList = records.stream().map(ProjectVo::getId).collect(Collectors.toList());
            List<CrmProjectItem> items = projectItemService.list(new LambdaQueryWrapper<CrmProjectItem>()
                    .in(CrmProjectItem::getProjectId, idList));
            if (CollectionUtils.isNotEmpty(items)) {
                Map<Long, List<CrmProjectItem>> itemsMap = items.stream().collect(Collectors.groupingBy(CrmProjectItem::getProjectId));
                records.stream().map(r -> {
                    if (itemsMap.containsKey(r.getId())) {
                        List<CrmProjectItem> projectItems = itemsMap.get(r.getId());
                        List<ProjectItemVo> itemBoList = projectItems.stream().map(x -> {
                            ProjectItemVo projectItemBo = new ProjectItemVo();
                            BeanUtils.copyProperties(x, projectItemBo);
                            return projectItemBo;
                        }).collect(Collectors.toList());
                        r.setProjectItemBoList(itemBoList);
                    }
                    return r;
                }).collect(Collectors.toList());

                projectVoIPage.setRecords(records);
            }
        }
        return BaseFxResponse.success(BasePageResult.newInstance(projectVoIPage));
    }

    /**
     * 1、可预约人数不能小于当前已预约人数
     * 2、如果项目处于启用状态，关联的场地的使用时间不能跟其他场地使用时间重合
     *
     * @param projectBo
     * @return
     */
    @Override
    public BaseFxResponse<Long> updateProject(ProjectBo projectBo) {
        CrmProject project = this.getById(projectBo.getId());

        if (projectBo.getMaxNumber() < project.getMaxNumber()) {
            int count = employeeProjectService.count(new LambdaQueryWrapper<CrmEmployeeProject>()
                    .eq(CrmEmployeeProject::getProjectId, project.getId())
                    .in(CrmEmployeeProject::getStatusCode, EmployeeProjectStatusEnum.queryValidStatus()));
            if (count > projectBo.getMaxNumber()) {
                throw new BusinessException(ResultCode.EXCEPTION_PROJECT_EMP_NUMBER.getCode(), ResultCode.EXCEPTION_PROJECT_EMP_NUMBER.getMessage());
            }
        }
        BeanUtils.copyProperties(projectBo, project);
        if (!checkSiteDate(project)) {
            throw new BusinessException(ResultCode.EXCEPTION_PROJECT_SITE_DATE.getCode(), ResultCode.EXCEPTION_PROJECT_SITE_DATE.getMessage());
        }
        this.updateById(project);
        return BaseFxResponse.success(projectBo.getId());
    }

    @Override
    public BaseFxResponse<Long> updateProjectStatus(Long projectId, Integer status) {
        ProjectStatusEnum statusEnum = ProjectStatusEnum.getByCode(status);
        if (statusEnum == null) {
            return BaseFxResponse.fail(EXCEPTION_STATUS_FAIL);
        }
        this.update(new LambdaUpdateWrapper<CrmProject>()
                .set(CrmProject::getProjectStatusCode, statusEnum.getCode())
                .set(CrmProject::getProjectStatusName, statusEnum.getValue())
                .eq(CrmProject::getId, projectId));
        return BaseFxResponse.success(projectId);
    }

    @Override
    public void projectStatusJob() {

        for (ProjectStatusEnum statusEnum : ProjectStatusEnum.values()) {
            log.info("开始执行>>{}", statusEnum.getValue());
            LambdaQueryWrapper<CrmProject> wrapper = new LambdaQueryWrapper<CrmProject>()
                    .ne(CrmProject::getProjectStatusCode, ProjectStatusEnum.PROJECT_CANCEL.getCode())
                    .ne(CrmProject::getProjectStatusCode, ProjectStatusEnum.PROJECT_END.getCode())
                    .ne(CrmProject::getProjectStatusCode, statusEnum.getCode())
                    .lt(CrmProject::getProjectStatusCode, statusEnum.getCode());
            if (ProjectStatusEnum.PROJECT_ORDER_IN == statusEnum) {
                wrapper.le(CrmProject::getOrderStarterTime, LocalDateTime.now());
            } else if (ProjectStatusEnum.PROJECT_ORDER_END == statusEnum) {
                wrapper.and(o -> o.le(CrmProject::getOrderEndTime, LocalDateTime.now())
                        .or().eq(CrmProject::getIsFullNumber, 1));
            } else if (ProjectStatusEnum.PROJECT_START == statusEnum) {
                wrapper.le(CrmProject::getProjectStarterTime, LocalDateTime.now());
            } else if (ProjectStatusEnum.PROJECT_END == statusEnum) {
                wrapper.le(CrmProject::getProjectEndTime, LocalDateTime.now());
            } else {
                continue;
            }

            List<CrmProject> projects = this.list(wrapper);
            if (CollectionUtils.isNotEmpty(projects)) {
                for (CrmProject project : projects) {
                    log.info("编号={}的项目由状态={}，进行到状态={}", project.getProjectCode(), project.getProjectStatusName(),
                            statusEnum.getValue());
                    project.setProjectStatusCode(statusEnum.getCode());
                    project.setProjectStatusName(statusEnum.getValue());
                    this.updateById(project);
                }
            }
        }
    }

    @Override
    public List<ReportCountVo> projectMonth(Integer code) {
        return baseMapper.projectMonth(code);
    }

    @Override
    public List<ReportCountVo> projectCount() {
        return baseMapper.projectCount();
    }

    /**
     * SELECT
     * *
     * FROM
     * crm_project
     * WHERE
     * site_id = 1
     * AND project_status_code != 1003105
     * AND project_status_code != 1003106
     * AND (
     * ( project_starter_time > '2022-01-01 00:00:00' AND project_starter_time < '2022-01-01 01:00:00' )
     * OR ( project_end_time > '2022-01-01 00:00:00' AND project_end_time < '2022-01-01 01:00:00' )
     * OR ( project_starter_time > '2022-01-01 00:00:00' AND project_end_time < '2022-01-01 01:00:00' )
     * )
     *
     * @param project
     * @return
     */
    public boolean checkSiteDate(CrmProject project) {
        Long siteId = project.getSiteId();
        LambdaQueryWrapper<CrmProject> wrapper = new LambdaQueryWrapper<CrmProject>()
                .eq(CrmProject::getSiteId, siteId)
                .ne(CrmProject::getProjectStatusCode, ProjectStatusEnum.PROJECT_END.getCode())
                .ne(CrmProject::getProjectStatusCode, ProjectStatusEnum.PROJECT_CANCEL.getCode())
                .and(y -> y.or(o -> o.gt(CrmProject::getProjectStarterTime, project.getProjectStarterTime()).lt(CrmProject::getProjectStarterTime, project.getProjectEndTime()))
                        .or(r -> r.gt(CrmProject::getProjectEndTime, project.getProjectStarterTime()).lt(CrmProject::getProjectEndTime, project.getProjectEndTime()))
                        .or(x -> x.gt(CrmProject::getProjectStarterTime, project.getProjectStarterTime()).lt(CrmProject::getProjectEndTime, project.getProjectEndTime())));
        if (project.getId() != null) {
            wrapper.ne(CrmProject::getId, project.getId());
        }
        List<CrmProject> crmProjects = this.list(wrapper);
        if (CollectionUtils.isNotEmpty(crmProjects)) {
            return false;
        }
        return true;
    }

    public static String createProjectNo() {

        LocalDateTime time = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
        String timeStr = formatter.format(time);
        StringBuffer stringBuffer = new StringBuffer("PR");
        stringBuffer.append(timeStr);
        return stringBuffer.toString();
    }


    public static void main(String[] args) {

        LocalDateTime time = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
        String timeStr = formatter.format(time);
        StringBuffer stringBuffer = new StringBuffer("PR");
        stringBuffer.append(timeStr);
        System.out.println(stringBuffer.toString());
        long l = System.currentTimeMillis();
        System.out.println(l);
    }
}
